/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package fork.nodeditor;

import fork.NodeEditor;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import tal.drivechain.chain.ChainNode;
import tal.drivechain.chain.ChainNode.MSG;
import tal.drivechain.chain.NodeListener;
import tal.drivechain.gui.GUITextBox;
import tal.drivechain.gui.TextButton;
import tal.drivechain.module.properties.ColorParser;
import tal.drivechain.module.properties.KeyValueParser;
import tal.drivechain.module.xmlconfig.PropertyInfo;
import tal.drivechain.plugin.Channel;
import tal.drivechain.variable.SetProperty;
import tal.drivechain.variable.SetProperty.Flag;
import tal.drivechain.variable.Variable;

/**
 *
 * @author Tal Eisenberg
 */
class PropertyEditor extends JPanel implements NodeListener {

    PropertyInfo prop;
    ChainNode node;
    private Color color;
    List<OnePropPanel> panels;
    TextButton addButton;
    private NodeEditor owner;

    public PropertyEditor(PropertyInfo prop, ChainNode node, Color color, NodeEditor owner) {
        super();
        this.owner = owner;
        panels = new ArrayList<OnePropPanel>();
        node.addNodeListener(this);
        this.prop = prop;
        this.node = node;
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        setColor(color);
        createEditor(node.getProperty(prop.getName()));
        this.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
    }

    public void createEditor(Object val) {
        if (prop.isRepeatable()) {
            if (val == null) {
                val = new ArrayList<Object>();
            }
            List<Object> vals = (List<Object>) val;
            for (Object v : vals) {
                addPropPanel(v);
            }
            addButton = new TextButton(" add " + prop.getName() + "... ", color, getFont());
            addButton.setNegative(true);
            addButton.addActionListener(new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    PropertyEditor.this.remove(addButton);
                    addPropPanel(null);
                    PropertyEditor.this.add(addButton);
                    PropertyEditor.this.revalidate();
                    PropertyEditor.this.repaint();
                }
            });
            add(addButton);

        } else {
            addPropPanel(val);
        }
        setFont(getFont());
    }

    public void updateProperty() {
        SetProperty[] setProps = new SetProperty[panels.size()];
        Flag flag = Flag.REPLACE;
        for (int i = 0; i < panels.size(); i++) {
            OnePropPanel p = panels.get(i);
            if (p.getKey().trim().length() != 0 && p.getValue().trim().length() != 0) {
                String sprop = p.getKey() + ": " + p.getValue();
                try {
                    setProps[i] = new SetProperty(node.getNodeClassName(), sprop, flag);
                    if (flag == Flag.REPLACE) {
                        flag = Flag.ADD;
                    }
                } catch (IllegalArgumentException ie) {
                    owner.log("while updating property: " + ie.getMessage(), MSG.ERROR);
                }

            }
        }

        for (SetProperty s : setProps) {
            if (s != null) {
                s.invokeSetProperty(node);
            }
        }
    }

    public void messageReceived(ChainNode src, Variable var, Channel channel, long timestamp) {
    }

    public void messageSent(ChainNode src, Variable var, long timestamp) {
    }

    public void propertyChanged(ChainNode src, String name, Object value) {
        if (name.equals(prop.getName())) {
            if (prop.isRepeatable()) {
                this.removeAll();
                panels.clear();
                revalidate();
                createEditor(node.getProperty(prop.getName()));
            } else {
                // in case of simple property update, we can just change the value of the single keyvalue panel.
                panels.get(0).setValue(prop.getParser().propToString(value, node));
            }

        }

        if (this.prop.getName().equals(name)) {
            this.setBorder(BorderFactory.createLineBorder(ColorParser.xor(color)));
            new Thread() {
                @Override
                public void run() {
                    try { sleep(300); } catch (InterruptedException ex) {}
                    PropertyEditor.this.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
                }
            }.start();
        }

    }

    public void connectionsChanged(ChainNode src) {
    }

    @Override
    public void setFont(Font font) {
        super.setFont(font);
        if (panels == null) {
            return;
        }
        for (Component c : this.getComponents()) {
            c.setFont(font);
        }
    }

    public void setColor(Color color) {
        this.color = color;
        this.setBackground(Color.BLACK);
        if (panels == null) {
            return;
        }
        for (OnePropPanel p : panels) {
            p.setColor(color);
        }
    }

    Color getColor() {
        return color;
    }

    private void addPropPanel(Object v) {
        OnePropPanel p;
        if (prop.getParser() instanceof KeyValueParser) {
            p = new KeyValuePropPanel(this);
            KeyValueParser kvParser = (KeyValueParser) prop.getParser();
            if (v != null) {
                Object key = ((Object[]) v)[0];
                Object value = ((Object[]) v)[1];
                p.setKey(kvParser.getKeyParser().propToString(key, node));
                p.setKeyTooltip(kvParser.getKeyParser().getPropertyType());
                p.setValue(kvParser.getValueParser().propToString(value, node));
                p.setValueTooltip(kvParser.getValueParser().getPropertyType());
            }
        } else {
            p = new ValOnlyPropPanel(this);
            p.setKey(prop.getName());
            p.setKeyTooltip(prop.getDescription());
            p.setValueTooltip(prop.getParser().getPropertyType());
            if (v != null) {
                p.setValue(prop.getParser().propToString(v, node));
            }
        }
        p.setFont(getFont());
        panels.add(p);
        add(p);
        p.requestFocus();

    }

    public void nameChanged(ChainNode src) {}

    public void metaChanged(ChainNode src) {}

    public void nodeDestroyed(ChainNode src) {}

    public void nodeMoved(ChainNode src) {}
}

abstract class OnePropPanel extends JPanel {

    PropertyEditor parent;

    public OnePropPanel(PropertyEditor parent) {
        this.setColor(parent.getColor());
        this.parent = parent;
        setBackground(Color.BLACK);
        initPanel();
    }

    public void setColor(Color color) {
    }

    public abstract void initPanel();

    public abstract void setKey(String key);

    public abstract void setValue(String value);

    public abstract void setKeyTooltip(String tooltip);

    public abstract void setValueTooltip(String tooltip);

    public abstract String getKey();

    public abstract String getValue();

    public class EnterListener extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_ENTER)
                parent.updateProperty();
        }
    }
}

class ValOnlyPropPanel extends OnePropPanel {

    JLabel keyLabel;
    GUITextBox valText;
    EditButton editButton;
    TextButton applyBtn;

    public ValOnlyPropPanel(PropertyEditor parent) {
        super(parent);
        this.setColor(parent.getColor());
    }

    @Override
    public void requestFocus() {
        super.requestFocus();
        valText.requestFocus();
    }

    @Override
    public void setColor(Color color) {
        super.setColor(color);
        if (keyLabel != null) {
            keyLabel.setForeground(color);
        }
        if (valText != null) {
            valText.setColor(color);
        }
        if (editButton != null) {
            editButton.setColor(color);
        }
    }

    @Override
    public void initPanel() {
        setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        keyLabel = new JLabel("<key>");
        keyLabel.setBackground(Color.BLACK);
        c.gridx = 0;
        c.weightx = 0;
        c.weighty = 0;
        c.insets = new Insets(1, 1, 1, 2);
        c.anchor = GridBagConstraints.WEST;
        add(keyLabel, c);

        valText = new GUITextBox();
        valText.setColumns(10);
        valText.setColor(parent.getColor());
        valText.initTextComponent();
        valText.addKeyListener(new EnterListener());
        c.gridx = 1;
        c.weightx = 1;
        c.weighty = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor = GridBagConstraints.EAST;
        c.insets = new Insets(1, 1, 1, 1);
        add(valText, c);

        applyBtn = new TextButton("✓", parent.getColor(), parent.getFont());
        applyBtn.setNegative(false);
        applyBtn.setToolTipText("apply");
        applyBtn.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                parent.updateProperty();
            }
        });

        c.gridx = 2;
        c.weightx = 0;
        c.weighty = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.insets = new Insets(1, 1, 1, 1);
        add(applyBtn, c);

        KeyListener kl = new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode()==KeyEvent.VK_ENTER) {
                    parent.updateProperty();
                }
            }
        };
        editButton = new EditButton(parent.getColor(), parent.getFont(), kl, valText);
        c.gridx = 3;
        c.weightx = 0;
        c.weighty = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        //c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets(0, 1, 0, 1);
        add(editButton, c);

        revalidate();
        repaint();
    }

    @Override
    public void setFont(Font font) {
        if (keyLabel == null) {
            return;
        }
        keyLabel.setFont(font);
        valText.setFont(font);
        editButton.setFont(font);
    }

    @Override
    public void setKey(String key) {
        keyLabel.setText(key);
    }

    @Override
    public void setValue(String value) {
        valText.setText(value);
        valText.getTextComponent().setCaretPosition(0);
    }

    @Override
    public String getKey() {
        return keyLabel.getText();
    }

    @Override
    public String getValue() {
        return valText.getText();
    }

    @Override
    public void setKeyTooltip(String tooltip) {
        keyLabel.setToolTipText(tooltip);
    }

    @Override
    public void setValueTooltip(String tooltip) {
        valText.setToolTipText(tooltip);
    }
}

class KeyValuePropPanel extends OnePropPanel {

    GUITextBox keyText, valText;
    EditButton enlargeBtn;
    TextButton applyBtn;

    public KeyValuePropPanel(PropertyEditor parent) {
        super(parent);
        this.setColor(parent.getColor());
        setBackground(Color.BLACK);
    }

    @Override
    public void setColor(Color color) {
        super.setColor(color);
        if (valText != null) valText.setColor(color) ;
        if (keyText != null) keyText.setColor(color);
        if (enlargeBtn!=null) enlargeBtn.setColor(color);

    }

    @Override
    public void requestFocus() {
        super.requestFocus();
        keyText.requestFocus();
    }

    @Override
    public void initPanel() {
        setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        keyText = new GUITextBox();
        keyText.setColumns(10);
        keyText.setBackground(Color.BLACK);
        keyText.setNegative(true);
        keyText.initTextComponent();
        c.gridx = 0;
        c.weightx = 0.2;
        c.weighty = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets(1, 1, 1, 2);
        add(keyText, c);

        valText = new GUITextBox();
        valText.setColumns(10);
        valText.setForeground(Color.BLACK);
        valText.initTextComponent();
        c.insets = new Insets(1, 1, 1, 1);
        c.gridx = 1;
        c.weightx = 0.8;
        c.weighty = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor = GridBagConstraints.WEST;
        add(valText, c);

        applyBtn = new TextButton("✓", parent.getColor(), parent.getFont());
        applyBtn.setToolTipText("apply");
        applyBtn.setNegative(false);
        applyBtn.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                parent.updateProperty();
            }
        });
        c.gridx = 2;
        c.weightx = 0;
        c.weighty = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.insets = new Insets(1, 1, 1, 1);
        add(applyBtn, c);

        enlargeBtn = new EditButton(parent.getColor(), parent.getFont(), new EnterListener(), keyText, valText);
        c.gridx = 3;
        c.weightx = 0;
        c.weighty = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor = GridBagConstraints.EAST;
        c.insets = new Insets(1, 1, 1, 1);
        add(enlargeBtn, c);

        revalidate();
        repaint();
    }

    @Override
    public void setFont(Font font) {
        super.setFont(font);
        if (keyText == null) {
            return;
        }
        keyText.setFont(font);
        valText.setFont(font);
        enlargeBtn.setFont(font);
    }

    @Override
    public void setKey(String key) {
        keyText.setText(key);
        keyText.getTextComponent().setCaretPosition(0);
    }

    @Override
    public void setValue(String value) {
        valText.setText(value);
        valText.getTextComponent().setCaretPosition(0);
    }

    @Override
    public String getKey() {
        return keyText.getText();
    }

    @Override
    public String getValue() {
        return valText.getText();
    }

    @Override
    public void setKeyTooltip(String tooltip) {
        keyText.setToolTipText(tooltip);
    }

    @Override
    public void setValueTooltip(String tooltip) {
        valText.setToolTipText(tooltip);
    }
}

class EditButton extends TextButton implements ActionListener {
    boolean enlarge = true;
    GUITextBox[] textBoxes;
    private KeyListener applyListener, nextFocusListener = new NextFocusListener();

    private void addKeyListeners() {
        for (int i=0; i<textBoxes.length-1; i++) {
            if (textBoxes[i].getRows()<=1) {
                textBoxes[i].addKeyListener(nextFocusListener);
            }

        } if (textBoxes[textBoxes.length-1].getRows()<=1) {
            textBoxes[textBoxes.length-1].addKeyListener(applyListener);
        }
    }

    class NextFocusListener extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode()==KeyEvent.VK_ENTER) {
                for (int i=0; i<textBoxes.length; i++) {
                    if (textBoxes[i].getTextComponent()==e.getSource()) {
                        if (i<textBoxes.length-1)
                            textBoxes[i+1].getTextComponent().requestFocus();
                    }
                }
            }
        }
    }

    public EditButton(Color c, Font f, KeyListener applyListener, GUITextBox... textBoxes) {
        super("+", c, f.deriveFont((float)(f.getSize()*0.8)));
        this.textBoxes = textBoxes;
        this.applyListener = applyListener;
        setNegative(false);
        addKeyListeners();
        addActionListener(this);
    }

    public void actionPerformed(ActionEvent e) {
        if (enlarge) {
            if (textBoxes!=null) {
                for (int i=0; i<textBoxes.length; i++) {
                    GUITextBox b = textBoxes[i];
                    b.setRows(5);
                    b.setColumns(b.getColumns()-1);
                    b.setWordWrap(true);
                    if (i<textBoxes.length-1)
                        b.getTextComponent().removeKeyListener(nextFocusListener);
                    else b.getTextComponent().removeKeyListener(applyListener);
                }
            }
            this.setText("-"); // minimize
            enlarge = false;
        } else {
            if (textBoxes!=null) {
                for (GUITextBox b : textBoxes) {
                    b.setRows(1);
                    b.setColumns(b.getColumns()+1);
                    b.setWordWrap(true);
                }
                this.addKeyListeners();
            }
            this.setText("+"); // enlarge
            enlarge = true;
        }

    }

}
